package com.example.dlna;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.jmdns.ServiceInfo;

/**
 * Class used to communicate to the AirPlay-enabled device.
 * 
 * @author copy
 */
public class AirPlayClientService {

	private Timer timer;

	// executor service for asynchronous tasks to the service
	private ExecutorService es;

	// callback back to UI
	private AirPlayClientCallback callback;

	/**
	 * Initialize the service.
	 * 
	 * @param callback
	 *            The callback back to the UI.
	 */
	public AirPlayClientService(AirPlayClientCallback callback) {
		this.callback = callback;
		es = Executors.newSingleThreadExecutor();
		timer = new Timer();
	}

	/**
	 * Shut down the service.
	 */
	public void shutdown() {
		if (es != null) {
			es.shutdown();
		}
	}

	/**
	 * Send an image file to the service (display the image).
	 * 
	 * @param file
	 *            The image file
	 * @param serviceInfo
	 *            The service to use
	 * @param transition
	 *            The image transition to use
	 * @throws Exception
	 *             If there are any problems with the parameters
	 */
	public void putImage(File file, ServiceInfo serviceInfo, String transition)
			throws Exception {
		if (serviceInfo == null) {
			throw new Exception("Not connected to AirPlay service");
		}
		es.submit(new PutImageTask(file, serviceInfo, transition));
	}

	/**
	 * Send a video link to the service (start playing).
	 * 
	 * @param location
	 *            The link to the video
	 * @param serviceInfo
	 *            The service to use
	 * @throws Exception
	 *             If there are any problems with the parameters
	 */
	public void playVideo(final URL location, final ServiceInfo serviceInfo)
			throws Exception {
		if (serviceInfo == null) {
			throw new Exception("Not connected to AirPlay service");
		}
		if (!location.equals(Cache.getCache().getUrl())) {
			stopVideo(serviceInfo);
		}
		timer.schedule(new TimerTask() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				es.submit(new PlayVideoTask(location, serviceInfo));
				Cache.getCache().setUrl(location);
			}
		}, 200);
	}

	/**
	 * Send a request to stop playing the current video.
	 * 
	 * @param serviceInfo
	 *            The service to use
	 * @throws Exception
	 *             If there are any problems with the parameters
	 */
	public void stopVideo(ServiceInfo serviceInfo) throws Exception {
		if (serviceInfo == null) {
			throw new Exception("Not connected to AirPlay service");
		}
		es.submit(new StopVideoTask(serviceInfo));
	}

	//
	// Private
	//

	private class PutImageTask implements Runnable {

		private File file;

		private ServiceInfo serviceInfo;

		private String transition;

		public PutImageTask(File file, ServiceInfo serviceInfo,
				String transition) {
			this.file = file;
			this.serviceInfo = serviceInfo;
			this.transition = transition;
		}

		@Override
		public void run() {
			try {
				URL url = new URL(serviceInfo.getURL() + "/photo");
				HttpURLConnection conn = (HttpURLConnection) url
						.openConnection();
				conn.setDoInput(true);
				conn.setDoOutput(true);
				conn.setConnectTimeout(15 * 1000);
				conn.setReadTimeout(15 * 1000);
				conn.setRequestMethod("PUT");
				conn.setRequestProperty("Content-Length", "" + file.length());
				conn.setRequestProperty("X-Apple-AssetKey", UUID.randomUUID()
						.toString());
				conn.setRequestProperty("X-Apple-Session-ID", UUID.randomUUID()
						.toString());
				conn.setRequestProperty("X-Apple-Transition", transition);
				conn.setRequestProperty("User-Agent", "MediaControl/1.0");
				BufferedOutputStream out = new BufferedOutputStream(
						conn.getOutputStream());
				BufferedInputStream in = new BufferedInputStream(
						new FileInputStream(file));
				byte[] buffer = new byte[32 * 1024];
				int i;
				while ((i = in.read(buffer)) != -1) {
					out.write(buffer, 0, i);
				}
				in.close();
				out.close();
				int status = conn.getResponseCode();
				if (status == 200) {
					callback.onPutImageSuccess(file);
				} else {
					callback.onPutImageError(file,
							"AirPlay service responded HTTP " + status);
				}
			} catch (Exception e) {
				callback.onPutImageError(file, e.getMessage());
			}
		}

	}

	private class PlayVideoTask implements Runnable {

		private URL location;

		private ServiceInfo serviceInfo;

		public PlayVideoTask(URL location, ServiceInfo serviceInfo) {
			this.location = location;
			this.serviceInfo = serviceInfo;
		}

		@Override
		public void run() {
			try {
				StringBuilder content = new StringBuilder();
				content.append("Content-Location: ");
				content.append(location.toString());
				content.append("\n");
				content.append("Start-Position: 0\n");
				System.err.println("url = " + serviceInfo.getURL().toString());
				URL url = new URL(serviceInfo.getURL() + "/play");
				HttpURLConnection conn = (HttpURLConnection) url
						.openConnection();
				conn.setDoInput(true);
				conn.setDoOutput(true);
				conn.setConnectTimeout(15 * 1000);
				conn.setReadTimeout(15 * 1000);
				conn.setRequestMethod("POST");
				conn.setRequestProperty("Content-Length", "" + content.length());
				conn.setRequestProperty("Content-Type", "text/parameters");
				conn.setRequestProperty("X-Apple-AssetKey", UUID.randomUUID()
						.toString());
				conn.setRequestProperty("X-Apple-Session-ID", UUID.randomUUID()
						.toString());
				conn.setRequestProperty("User-Agent", "MediaControl/1.0");
				BufferedOutputStream out = new BufferedOutputStream(
						conn.getOutputStream());
				out.write(content.toString().getBytes());
				out.close();
				int status = conn.getResponseCode();
				if (status == 200) {
					callback.onPlayVideoSuccess(location);
				} else {
					callback.onPlayVideoError(location,
							"AirPlay service responded HTTP " + status);
				}
			} catch (Exception e) {
				callback.onPlayVideoError(location, e.getMessage());
			}
		}

	}

	private class StopVideoTask implements Runnable {

		private ServiceInfo serviceInfo;

		public StopVideoTask(ServiceInfo serviceInfo) {
			this.serviceInfo = serviceInfo;
		}

		@Override
		public void run() {
			try {
				URL url = new URL(serviceInfo.getURL() + "/stop");
				HttpURLConnection conn = (HttpURLConnection) url
						.openConnection();
				conn.setConnectTimeout(15 * 1000);
				conn.setReadTimeout(15 * 1000);
				conn.setRequestMethod("POST");
				conn.setRequestProperty("Content-Length", "0");
				conn.setRequestProperty("User-Agent", "MediaControl/1.0");
				int status = conn.getResponseCode();
				if (status == 200) {
					callback.onStopVideoSuccess();
				} else {
					callback.onStopVideoError("AirPlay service responded HTTP "
							+ status);
				}
			} catch (Exception e) {
				callback.onStopVideoError(e.getMessage());
			}
		}

	}

}
